package com.zx.flow.api.config;

import com.google.common.collect.Sets;
import com.zx.flow.api.exception.FlowException;
import com.zx.flow.api.util.AssertUtil;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.NoArgsConstructor;

/**
 * 配置校验流程
 */
public abstract class FlowConfigCheckChain {

    abstract void chain(FlowConfig config);

    public void check(FlowConfig config) {

    }

    @NoArgsConstructor
    public static class Check1 extends FlowConfigCheckChain {

        @Override
        void chain(FlowConfig config) {
            // 1.node code不能重复
            Map<String, List<FlowConfigNode>> nodeCodeMap = config.getNodes().stream()
                    .collect(Collectors.groupingBy(FlowConfigNode::getCode));
            for (Entry<String, List<FlowConfigNode>> entry : nodeCodeMap.entrySet()) {
                String nodeCode = entry.getKey();
                if (entry.getValue().size() > 1) {
                    AssertUtil.throwFE("nodeCode不能重复, FlowCode=" + config.getFlowCode() + "nodeCode=" + nodeCode);
                }
            }
        }
    }

    @NoArgsConstructor
    public static class Check2 extends FlowConfigCheckChain {

        @Override
        void chain(FlowConfig config) {
            //2.只有一个start和end
            long startNum = config.getNodes().stream().filter(node -> node.isStart()).count();
            long endNum = config.getNodes().stream().filter(node -> node.isEnd()).count();
            if (startNum != 1 || endNum != 1) {
                throw new FlowException("start和end节点都必须只有一个, startNum=" + startNum + ", endNum=" + endNum);
            }
            if (config.getNodes().size() <= 2) {
                throw new FlowException("至少有一个正常节点");
            }
        }
    }

    @NoArgsConstructor
    public static class Check3 extends FlowConfigCheckChain {

        @Override
        void chain(FlowConfig config) {
            //3.校验单个node中，是否构成内部循环 node1-> node1
            for (FlowConfigNode node : config.getNodes()) {
                Set<String> allTos = this.getToCode(node);
                if (allTos.contains(node.getCode())) {
                    throw new FlowException("本节点的nodeCode与toNodeCode形成内部循环, nodeCode=" + node.getCode());
                }
            }
        }

        private Set<String> getToCode(FlowConfigNode node) {
            // 结束节点 不需要 to
            if (node.isEnd()) {
                return Collections.emptySet();
            }
            AssertUtil.checkNotEmpty(node.getAction(), "非结束节点配置必须设置action");
            Set<String> set = Sets.newHashSet();
            for (FlowConfigAction action : node.getAction()) {
                AssertUtil.checkNotEmpty(action.getTo(), "非结束节点配置必须设置to");
                for (FlowConfigToNode to : action.getTo()) {
                    set.add(to.getCode());
                }
            }
            return set;
        }
    }


}
